/* eslint-disable promise/prefer-await-to-then */

const methodMap = [
  [
    'requestFullscreen',
    'exitFullscreen',
    'fullscreenElement',
    'fullscreenEnabled',
    'fullscreenchange',
    'fullscreenerror'
  ],
  // npm
  // New WebKit
  [
    'webkitRequestFullscreen',
    'webkitExitFullscreen',
    'webkitFullscreenElement',
    'webkitFullscreenEnabled',
    'webkitfullscreenchange',
    'webkitfullscreenerror'

  ],
  // Old WebKit
  [
    'webkitRequestFullScreen',
    'webkitCancelFullScreen',
    'webkitCurrentFullScreenElement',
    'webkitCancelFullScreen',
    'webkitfullscreenchange',
    'webkitfullscreenerror'

  ],
  [
    'mozRequestFullScreen',
    'mozCancelFullScreen',
    'mozFullScreenElement',
    'mozFullScreenEnabled',
    'mozfullscreenchange',
    'mozfullscreenerror'
  ],
  [
    'msRequestFullscreen',
    'msExitFullscreen',
    'msFullscreenElement',
    'msFullscreenEnabled',
    'MSFullscreenChange',
    'MSFullscreenError'
  ]
]

const nativeAPI = (() => {
  const unprefixedMethods = methodMap[0]
  const returnValue = {}

  for (const methodList of methodMap) {
    const exitFullscreenMethod = methodList?.[1]
    if (exitFullscreenMethod in document) {
      for (const [index, method] of methodList.entries()) {
        returnValue[unprefixedMethods[index]] = method
      }

      return returnValue
    }
  }

  return false
})()

const eventNameMap = {
  change: nativeAPI.fullscreenchange,
  error: nativeAPI.fullscreenerror
}

// eslint-disable-next-line import/no-mutable-exports
let screenfull = {
  // eslint-disable-next-line default-param-last
  request(element = document.documentElement, options) {
    return new Promise((resolve, reject) => {
      const onFullScreenEntered = () => {
        screenfull.off('change', onFullScreenEntered)
        resolve()
      }

      screenfull.on('change', onFullScreenEntered)

      const returnPromise = element[nativeAPI.requestFullscreen](options)

      if (returnPromise instanceof Promise) {
        returnPromise.then(onFullScreenEntered).catch(reject)
      }
    })
  },
  exit() {
    return new Promise((resolve, reject) => {
      if (!screenfull.isFullscreen) {
        resolve()
        return
      }

      const onFullScreenExit = () => {
        screenfull.off('change', onFullScreenExit)
        resolve()
      }

      screenfull.on('change', onFullScreenExit)

      const returnPromise = document[nativeAPI.exitFullscreen]()

      if (returnPromise instanceof Promise) {
        returnPromise.then(onFullScreenExit).catch(reject)
      }
    })
  },
  toggle(element, options) {
    return screenfull.isFullscreen ? screenfull.exit() : screenfull.request(element, options)
  },
  onchange(callback) {
    screenfull.on('change', callback)
  },
  onerror(callback) {
    screenfull.on('error', callback)
  },
  on(event, callback) {
    const eventName = eventNameMap[event]
    if (eventName) {
      document.addEventListener(eventName, callback, false)
    }
  },
  off(event, callback) {
    const eventName = eventNameMap[event]
    if (eventName) {
      document.removeEventListener(eventName, callback, false)
    }
  },
  raw: nativeAPI
}

Object.defineProperties(screenfull, {
  isFullscreen: {
    get: () => Boolean(document[nativeAPI.fullscreenElement])
  },
  element: {
    enumerable: true,
    get: () => document[nativeAPI.fullscreenElement] ?? undefined
  },
  isEnabled: {
    enumerable: true,
    // Coerce to boolean in case of old WebKit.
    get: () => Boolean(document[nativeAPI.fullscreenEnabled])
  }
})

if (!nativeAPI) {
  screenfull = { isEnabled: false }
}

export default screenfull
